package com.rw.engine.ip.interceptor;

import com.rw.engine.ip.anno.IpLimit;
import com.rw.engine.ip.enumeration.FilterMode;
import com.rw.engine.ip.enumeration.MatchMode;
import com.rw.engine.ip.exception.IpInterceptorException;
import com.rw.engine.ip.util.IpUtil;
import com.rw.tool.util.string.StringUtil;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * IP验证拦截器默认切片
 * 可以自定义IpGetter
 * 如果需要自定义IpGetter，则需要把这个FilterRule放入Spring容器中
 */
public class DefaultIpFilterRule implements IpFilterRule {
    private final IpGetter ipGetter;

    public DefaultIpFilterRule(IpGetter ipGetter) {
        this.ipGetter = ipGetter;
    }

    public DefaultIpFilterRule() {
        // 默认使用默认的IP获取
        this.ipGetter = ServletRequest::getRemoteAddr;
    }

    public void filter(IpLimit ipLimit, HttpServletRequest request) {
        // 获取IP
        String ip;
        if (ipGetter == null) {
            ip = request.getRemoteAddr();
        } else {
            ip = ipGetter.getIp(request);
        }
        // 校验IP
        if (!IpUtil.isValid(ip)) {
            throw new IpInterceptorException("Current IP is invalid", ip);
        }
        // 获得注解上的ip模版 合并
        Set<String> ipTemplateSet = mergeIpArray(ipLimit.value(), ipLimit.ip());
        // 获得匹配模式
        MatchMode matchMode = ipLimit.matchMode();
        boolean whiteList = ipLimit.filterMode() == FilterMode.WHITELIST;
        boolean match = ipTemplateSet.stream()
                .anyMatch(template -> verifyByMatchMode(ip, template, matchMode));
        // 白名单模式并不匹配，或黑名单模式并匹配时
        // 也就是whiteList和match不同时
        if (whiteList != match) {
            throw new IpInterceptorException("Current IP is forbidden!", ip);
        }
    }

    /**
     * 判断单个IP和Template是否匹配
     */
    private boolean verifyByMatchMode(String ip, String template, MatchMode matchMode) {
        switch (matchMode) {
            case WILDCARD:
                return IpUtil.verify(ip, template);
            case REGEX:
                return IpUtil.match(ip, template);
        }
        return false;
    }


    /**
     * 将多个ip数组合并为一个HashSet 方便遍历
     */
    private Set<String> mergeIpArray(String[]... ips) {
        return Arrays.stream(ips)
                .flatMap(Arrays::stream)
                .filter(ip -> !StringUtil.isEmpty(ip))
                .collect(Collectors.toSet());
    }
}
